﻿using System;
using System.Collections.Generic;
using System.Text;
using UnityEditor; 
using UnityEditor.IMGUI.Controls;
using UnityEditor.PackageManager;
using UnityEditor.PackageManager.Requests;
using UnityEditorInternal;
using UnityEngine; 

namespace XFGameFramework
{
    public class ExpansionWindow : EditorWindow
    {
        // Fix编码

        #region 字段

        private ExpansionPackages _packages = null;
        private Rect left;
        private Rect right_bottom;

        private Vector2 scrollViewLeft;
        private Vector2 scrollViewRight;

        private TreeViewState packageListState;

        private ExpansionPackageListTree packageListTree;
         
        private ListRequest requestList;

        private int index;
        private float timer;

        private ExpansionPackageInfo currentShowInfo;

        private GUIStyle headerStyle;
        
        private GUIStyle nameStyle;

        private GUIStyle linkStyle;

        private GUIStyle desStyle;

        private Dictionary<string, SearchRequest> searchRequests = new Dictionary<string, SearchRequest>();

        private Color color_dark = new Color(0.2470588f, 0.2470588f, 0.2470588f,1);
        private Color color_white = new Color(0.8352942f, 0.8352942f, 0.8352942f, 1);

        private AddRequest requestAdd = null;
        private string requestName = null;
        private string currentRequestName = null;

        private RemoveRequest requestRemove = null;

        private Dictionary<string, int> packageCount = new Dictionary<string, int>();

        private StringBuilder versionBuilder = new StringBuilder();

        private float installerTimer = 0;

        private bool in_installation = false;

        private bool in_remove = false;

        private float removeTimer = 0;

        #endregion

        #region 属性

        internal ExpansionPackages Packages
        {
            get
            {

                if (_packages == null)
                    _packages = AssetDatabase.LoadAssetAtPath<ExpansionPackages>("Packages/com.xfkj.xfgameframework/Editor/Configs/ExpansionPackages.asset");

                if (_packages == null)
                    _packages = AssetDatabase.LoadAssetAtPath<ExpansionPackages>("Assets/Editor/Configs/ExpansionPackages.asset");

                return _packages;
            }
        }

        internal ListRequest RequestList => requestList;

        private Color right_bottom_color {
            get {
                if (!EditorGUIUtility.isProSkin) { 
                    return color_white;
                }
                return color_dark;
            }
        }

        #endregion


        [MenuItem("Window/XFKT/XFGameFramework/拓展包",false,0)]
        static void Open()
        {
            GetWindow<ExpansionWindow>().Show();
        }

        private void Awake()
        {
            titleContent = new GUIContent("XFGameFramework拓展包", "XFGameFramework拓展包");
            minSize = new Vector2(500, 300);


        }

        private void Update()
        {
            Repaint();

            HandleAdd();

            HandleRemove();
        }


        private void OnGUI()
        {
            if (requestList == null)
                Refresh();

            switch (requestList.Status)
            {
                case StatusCode.InProgress:
                    Loading();
                    break;
                case StatusCode.Success: 
                    GUI();  
                    break;
                case StatusCode.Failure:
                    LoadingFailure();
                    break;
            }



        }

        internal void Refresh()
        {
            requestList = Client.List();
        }

        private void Loading()
        {
            GUILayout.FlexibleSpace();

            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();


            GUIContent content = EditorGUIUtility.IconContent(string.Format("d_WaitSpin{0:00}", index));


            if (Time.realtimeSinceStartup - timer > 0.1f)
            {
                index++;
                index %= 10;
                timer = Time.realtimeSinceStartup;
            }

            GUILayout.Label(content, "LargeLabel");

            GUILayout.Label("Loading packages...", "LargeLabel");


            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();

            GUILayout.FlexibleSpace();
        }

        private void LoadingFailure()
        {
            //


            GUILayout.FlexibleSpace();

            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();

            GUILayout.Label(string.Empty, "CN EntryErrorIcon");

            GUILayout.BeginVertical();

            GUILayout.Space(15);

            if (requestList.Error != null)
            {
                string message = string.Format("errorCode:{0} message:{1}", requestList.Error.errorCode, requestList.Error.message);
                GUILayout.Label(message, "WhiteLargeCenterLabel");
            }


            GUILayout.EndVertical();

            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();

            GUILayout.Space(15);

            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            if (GUILayout.Button("重试", GUILayout.Width(100)))
            {
                Refresh();
            }
            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();

            GUILayout.FlexibleSpace();

        }

        private void GUI()
        {
            left.Set(0, 0, this.position.width * 0.35f, this.position.height);
            
            if (packageListState == null)
            {
                packageListState = new TreeViewState();
            }

            if (packageListTree == null)
            {
                packageListTree = new ExpansionPackageListTree(packageListState, this);
                packageListTree.Reload();
                packageListTree.OnCreate();
            }

            
            packageListTree.OnGUI(left);

            scrollViewRight = GUILayout.BeginScrollView(scrollViewRight);

            GUILayout.BeginHorizontal();

            GUILayout.Space(left.width + 10);

            GUILayout.BeginVertical();

            GUILayout.Space(10);

            if (currentShowInfo == null && Packages.packages != null && Packages.packages.Count > 0)
                currentShowInfo = Packages.packages[0];

            if (currentShowInfo == null)
                return;

            if (headerStyle == null) {
                headerStyle = new GUIStyle("AM HeaderStyle");
                headerStyle.fontSize = 16;
            }

            if (nameStyle == null) {
                nameStyle = new GUIStyle("WordWrappedLabel");
                nameStyle.fontSize = 15;
                nameStyle.fontStyle = FontStyle.Italic;
            }


            GUILayout.Label(currentShowInfo.displayName, "AM MixerHeader2");
            GUILayout.Space(10);

            versionBuilder.Clear();

            if (IsInstalled(currentShowInfo.name) && GetPackage(currentShowInfo.name) != null)
                versionBuilder.Append("Version: ").Append(GetPackage(currentShowInfo.name)?.version); 
            else 
                versionBuilder.Append("Version: ").Append(currentShowInfo.version);

            versionBuilder.Append("     最低支持 Unity 版本:").Append(currentShowInfo.min_unity_version);

            GUILayout.Label(versionBuilder.ToString(), "ProfilerHeaderLabel");


            GUILayout.Label("名称", headerStyle);
            EditorGUILayout.SelectableLabel(currentShowInfo.name, nameStyle);

            GUILayout.Space(10);

            GUILayout.Label("链接", headerStyle);

            DrawLink("视频介绍", currentShowInfo.video_url);
            DrawLink("快速入门", currentShowInfo.quick_start);
            DrawLink("API文档", currentShowInfo.api);
            DrawLink("CHANGE LOG", currentShowInfo.change_log);
            DrawLink("插件源码", currentShowInfo.gitUrl);
            DrawLink("实战项目", "https://space.bilibili.com/258939476/pugv");

            GUILayout.Label("作者", headerStyle);
            GUILayout.Label(currentShowInfo.author, "ProfilerHeaderLabel");

            GUILayout.Space(10);

            if (desStyle == null) 
            {
                desStyle = new GUIStyle("WordWrappedLabel");
                desStyle.fontSize = 14;
            }

            GUILayout.Label(currentShowInfo.description, desStyle);

            GUILayout.Space(10);

            GUILayout.Label("依赖", headerStyle);
            GUILayout.Space(10);
            DrawDependencies();

            
            GUILayout.Space(50);
            GUILayout.EndVertical();
            GUILayout.EndHorizontal();
            GUILayout.EndScrollView();

            DrawBottom();
        }


        internal bool IsInstalled(string name)
        {
             
            if (RequestList == null)
                return false;

            if (RequestList.Status != StatusCode.Success)
                return false;

            foreach (var item in RequestList.Result)
            { 
                if (item.name == name)
                    return true;
            }

            string dir = string.Format("Packages/{0}", name);  
            return AssetDatabase.IsValidFolder(dir);
        }

        internal UnityEditor.PackageManager.PackageInfo GetPackage(string name) {
            if (RequestList == null)
                return null;

            if (RequestList.Status != StatusCode.Success)
                return null;

            foreach (var item in RequestList.Result)
            {
                if (item.name == name)
                    return item;
            } 
            return null;
        }

        internal void ShowInfo(ExpansionPackageInfo info)
        {
            currentShowInfo = info;

            foreach (var item in Packages.packages)
            {
                item.selected = false;
            }
            currentShowInfo.selected = true;
            Packages.Save(); 
        }
        private void DrawLink(string title, string url)
        {
            GUILayout.BeginHorizontal();
            //GUILayout.Label(title, GUILayout.Width(60));

            if (linkStyle == null) {
                linkStyle = new GUIStyle(UnityEngine.GUI.skin.label);
                linkStyle.richText = true;
                linkStyle.normal.textColor = new Color(0.03f, 0.4f, 0.9f, 1);
                linkStyle.onHover.textColor = Color.white;
                linkStyle.alignment = TextAnchor.MiddleLeft;
                linkStyle.fontStyle = FontStyle.Italic;
            }

            if (GUILayout.Button(title, linkStyle))
            {
                Application.OpenURL(url);
            }
            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();
        }

        private void DrawDependencies() {

            if (currentShowInfo.dependencies == null || currentShowInfo.dependencies.Count == 0)
                GUILayout.Label("无依赖", "AnimationTimelineTick");
            else
            {
                foreach (var name in currentShowInfo.dependencies)
                {
                    if (string.IsNullOrEmpty(name)) continue;
                    if (!searchRequests.ContainsKey(name))
                    {
                        SearchRequest request = Client.Search(name);
                        searchRequests.Add(name, request);
                    }

                    if (searchRequests.ContainsKey(name) && searchRequests[name].Status == StatusCode.InProgress)
                    {
                        GUILayout.Label("loading...", "AnimationTimelineTick");
                    }

                    if (searchRequests.ContainsKey(name) && searchRequests[name].IsCompleted)
                    {
                        SearchRequest r = searchRequests[name];
                        UnityEditor.PackageManager.PackageInfo packageInfo = null;
                        if (r.Result != null && r.Result.Length > 0)
                        {
                            packageInfo = r.Result[0];
                        }

                        if (packageInfo == null)
                            packageInfo = GetPackage(name); 


                        if (packageInfo != null)
                        {
                            GUILayout.BeginHorizontal();
                            // 查到了 
                            GUILayout.Label(packageInfo.displayName,"AnimationTimelineTick", GUILayout.Width(200));
                            
                            if (IsInstalled(name))
                            {
                                GUILayout.Label(packageInfo.version, "AnimationTimelineTick", GUILayout.Width(200));
                                GUILayout.Label("已安装", "AnimationTimelineTick");
                            }
                            else
                            {
                                GUILayout.Label("未安装", "AnimationTimelineTick");
                            }

                            GUILayout.EndHorizontal();
                        }
                        else
                        {
                            // 没查到判断本地 有没有

                            ExpansionPackageInfo info = Packages.Get(name);

                            if (info != null)
                            {
                                GUILayout.BeginHorizontal();
                                // 查到了 
                                GUILayout.Label(info.displayName, "AnimationTimelineTick", GUILayout.Width(200));

                                if (IsInstalled(name))
                                {
                                    GUILayout.Label(info.version, "AnimationTimelineTick", GUILayout.Width(200));
                                    GUILayout.Label("已安装", "AnimationTimelineTick");
                                }
                                else
                                {
                                    GUILayout.Label("未安装", "AnimationTimelineTick");
                                }

                                GUILayout.EndHorizontal();
                            }
                            //else
                            //{
                            //    GUILayout.BeginHorizontal();
                            //    // 查到了 
                            //    GUILayout.Label(name);
                            //    GUILayout.Label("未知依赖", "AnimationTimelineTick");
                            //    GUILayout.EndHorizontal();
                            //}

                        }
                    }

                }
            }

        }


        private void DrawBottom() 
        {

            right_bottom.Set(this.position.width * 0.35f, this.position.height - 30, this.position.width * 0.65f, 30);

            EditorGUI.DrawRect(right_bottom, right_bottom_color);

            if (IsInstalled(currentShowInfo.name))
            {
                // 更新和移除
                right_bottom.Set(right_bottom.x + right_bottom.width - 75, right_bottom.y + 5, 70, right_bottom.height - 10);

                EditorGUI.BeginDisabledGroup(currentShowInfo.cannot_remove);
                if (UnityEngine.GUI.Button(right_bottom, "移除")) 
                    OnBtnRemoveClick(); 
                EditorGUI.EndDisabledGroup();

                right_bottom.Set(right_bottom.x - 75, right_bottom.y, 70, right_bottom.height);

                if (UnityEngine.GUI.Button(right_bottom, "更新")) {
                    OnBtnInstallClick();
                }

            }
            else {
                // 安装
                right_bottom.Set(right_bottom.x +  right_bottom.width - 75, right_bottom.y + 5, 70, right_bottom.height - 10);
                if (UnityEngine.GUI.Button(right_bottom, "安装"))
                {
                    OnBtnInstallClick();
                }

            }

            //UnityEngine.GUI.Button()

            right_bottom.Set(this.position.width * 0.35f, this.position.height - 60, this.position.width * 0.65f, 30);

            UnityEngine.GUI.Box(right_bottom, string.Empty, "CN Box");
        }

        private void HandleAdd() 
        { 
            if (Packages.need_adds.Count > 0 && !in_installation)
            {

                if (Time.realtimeSinceStartup - installerTimer <= 1) 
                { 
                    EditorUtility.DisplayProgressBar("安装拓展包", string.Format("正在安装:{0}", Packages.need_adds[0]), 1);
                    return;
                } 

                requestName = Packages.need_adds[0];
                Packages.need_adds.RemoveAt(0);
                Packages.Save();
                requestAdd = Client.Add(requestName);

                installerTimer = Time.realtimeSinceStartup;

                in_installation = true;

                //Debug.LogFormat("开始安装:{0}", requestName);
            }

            if (!in_installation)  
                return; 

            EditorUtility.DisplayProgressBar("安装拓展包", string.Format("正在安装:{0}", requestName), 1);

            if (requestAdd != null && requestAdd.IsCompleted)
            {
                EditorUtility.ClearProgressBar();
                
                installerTimer = Time.realtimeSinceStartup;

                //Debug.LogFormat("安装完成:{0} name:{1} error:{2}",requestAdd.Status,requestName,requestAdd.Error?.message);

                if (requestAdd.Status == StatusCode.Success)
                {
                    in_installation = false;
                    if (Packages.need_adds.Count == 0)
                        Refresh();

                    requestName = null;
                    requestAdd = null;
                }
                else if (requestAdd.Status == StatusCode.Failure)
                {
                    if (requestName.StartsWith("http"))
                    {
                        in_installation = false;
                          
#if UNITY_2019_1_OR_NEWER && !UNITY_2021_1_OR_NEWER
                        if (!IsInstalled(currentRequestName))
                        { 
                            string message = string.Format("{0}安装失败,errorCode:{1},message:{2},请稍后再试!", requestName, requestAdd.Error?.errorCode, requestAdd.Error?.message);
                            this.ShowNotification(new GUIContent(message));
                            Debug.LogError(message);
                        }
#else 
                        string message = string.Format("{0}安装失败,errorCode:{1},message:{2},请稍后再试!", requestName, requestAdd.Error?.errorCode, requestAdd.Error?.message);
                        this.ShowNotification(new GUIContent(message));
                        Debug.LogError(message);
#endif

                        requestName = null;
                        requestAdd = null;
                    }
                    else
                    {
                        ExpansionPackageInfo info = Packages.Get(requestName);

                        if (info != null)
                        {
                            currentRequestName = requestName;
                            requestName = info.gitUrl;
                            requestAdd = Client.Add(info.gitUrl);
                        }
                        else
                        {
                            in_installation = false;
                            string message = string.Format("{0}安装失败,errorCode:{1},message:{2},请稍后再试!", requestName, requestAdd.Error?.errorCode, requestAdd.Error?.message);
                            this.ShowNotification(new GUIContent(message));
                            Debug.LogError(message);
                            requestName = null;
                            requestAdd = null;
                        }
                    }

                    // 通过名称安装失败 尝试通过git安装

                }

            }
        }

        private void HandleRemove() 
        {
            if (Packages.need_removes.Count > 0 && !in_remove)
            {
                if (Time.realtimeSinceStartup - removeTimer < 1)
                    return;

                string removeName = Packages.need_removes[Packages.need_removes.Count - 1];
                Packages.need_removes.RemoveAt(Packages.need_removes.Count - 1);
                Packages.Save();
                requestRemove = Client.Remove(removeName);
                in_remove = true;
                removeTimer = Time.realtimeSinceStartup;
            }

            if (requestRemove != null && requestRemove.IsCompleted) 
            {  
                if (Packages.need_removes.Count == 0)
                    Refresh(); 
                requestRemove = null;
                in_remove = false;


                removeTimer = Time.realtimeSinceStartup;
            }

        }


        /// <summary>
        /// 判断是否被依赖
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        private bool IsBeDependentOn(string name,out string displayName) {

            foreach (var item in Packages.packages)
            {
                if (IsInstalled(item.name)) { 
                    foreach (var dependency in item.dependencies)
                    {
                        if (dependency == name) {
                            displayName = item.displayName;
                            return true;
                        }
                    }
                }
            }
            displayName = string.Empty;
            return false;
        }


        private void OnBtnInstallClick() 
        {
            if (IsVersionLower(currentShowInfo.min_unity_version))
            {
                string tip = string.Format("当前编辑器版本:{0} 低于该拓展包支持的最低版本:{1},建议您使用更高版本的Unity3D编辑器!",Application.unityVersion,currentShowInfo.min_unity_version);
                bool b = EditorUtility.DisplayDialog("提示", tip, "仍要安装", "取消");
                if (!b) return;
            }
             
            Packages.need_adds.Clear();
            packageCount.Clear();

            List<string> packages = GetPackageName(currentShowInfo.name);
            if(packages != null)
                Packages.need_adds.AddRange(packages);
            Packages.Save();

            //foreach (var item in Packages.need_adds)
            //{
            //    Debug.LogFormat("安装:{0}",item); 
            //}

        }

        private void OnBtnRemoveClick() {
            string d = null;
            if (IsBeDependentOn(currentShowInfo.name, out d))
            {
                string message = string.Format("拓展包:{0}不能移除,{1}依赖于它!", currentShowInfo.name, d);
                EditorUtility.DisplayDialog("提示", message, "确定");
                Debug.LogError(message);
                return;
            }

            // 判断
            Packages.need_removes.Clear();

            List<string> list = GetPackageName(currentShowInfo.name); 
            Packages.need_removes.AddRange(list);
            Packages.Save();
        }

        public List<string> GetPackageName(string name) 
        {

            if (packageCount.ContainsKey(name))
                packageCount[name]++;
            else
                packageCount.Add(name, 1);

            if (packageCount[name] > 100) 
            {
                StringBuilder builder = new StringBuilder();
                builder.AppendLine("循环依赖,下列包:");
                foreach (var packageName in packageCount.Keys)
                {
                    if (packageCount[packageName] > 90)
                    {
                        builder.AppendLine(packageName);    
                    }
                }
                builder.AppendLine("之前形成了循环依赖,请调整后重试!");

                this.ShowNotification(new GUIContent(builder.ToString()));
                throw new System.Exception(builder.ToString());
            }

            if(string.IsNullOrEmpty(name)) 
                return null;

            ExpansionPackageInfo info = Packages.Get(name);

            List<string> packages = new List<string>();

            if (info == null) 
            {
                packages.Add(name);
                return packages;
            }


            foreach (var item in info.dependencies)
            {
                List<string> d = GetPackageName(item);
                if (d != null)
                {
                    foreach (var dependency in d)
                    {
                        if (packages.Contains(dependency))
                            continue;
                        packages.Add(dependency);
                    }
                }
            }
            if(!packages.Contains(name))
                packages.Add(name);

            return packages;
            

        }


        public static bool IsVersionLower(string version)
        {

            if (!version.Contains(".")) return false;
            
            string[] strs = version.Split('.');

            if (strs.Length < 3) return false;
             
            Version v = InternalEditorUtility.GetUnityVersion(); 

            int major = 0;
            int.TryParse(strs[0],out major);

            if (v.Major > major)
                return false;
            else if (v.Major < major)
                return true;

            int minor = 0;
            int.TryParse(strs[1], out minor);

            if (v.Minor < minor)
                return true;
            else if (v.Minor > minor)
                return false;

            int build = 0;
            int.TryParse(strs[2], out build);

            if (v.Build < build) return true;

            return false;
        }

    }
}

